package stat

import (
	"sort"
	"strings"

	"gddgame.cc/galaxy/common/dsl/game"
	. "gddgame.cc/galaxy/common/dsl/game"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/def"
	"gddgame.cc/galaxy/utils/orm"
)

type StatSave interface {
	GetStatConfigByKey(key string) []utils.StatConfig
	GetStatData(context def.Context, id int) map[utils.CycleKey]map[string]int32
	SaveStatData(context def.Context, id int, key string, value int32, reset bool) error
}

type StatInfo struct {
	id   int
	Auto bool
	StatSave
	// 完整统计信息
	Stats        bool
	AllStats     map[string]int32
	DailyStats   map[string]int32
	WeeklyStats  map[string]int32
	MonthlyStats map[string]int32
}

func NewStatInfo(save StatSave, id int) (*StatInfo, error) {
	data := &StatInfo{id: id, StatSave: save, Auto: true}
	return data, nil
}

// 判断条件是否达成
func (info *StatInfo) JudgeFactor(factors []Factor) bool {
	for _, factor := range factors {
		if !info.JudgeFactorSingle(factor) {
			return false
		}
	}
	return true
}

func (info *StatInfo) GoalFactor(factors []Factor) int {
	goal := int32(0)
	for _, factor := range factors {
		g := info.GoalFactorSingle(factor)
		if factor.Value < g {
			g = factor.Value
		}
		goal += g
	}
	return int(goal)
}

func (info *StatInfo) GoalFactorSingle(factor Factor) int32 {
	var source map[string]int32
	// relation是对比数字
	// source需要转化为数字条件
	switch factor.Source {
	case DailySource:
		source = info.DailyStats
	case WeeklySource:
		source = info.WeeklyStats
	case MonthlySource:
		source = info.MonthlyStats
	default:
		source = info.AllStats
		return 0
	}

	if v, ok := source[factor.Key]; ok {
		return v
	} else {
		return 0
	}
}

func (info *StatInfo) JudgeFactorSingle(factor Factor) bool {
	var source map[string]int32
	// relation是对比数字
	// source需要转化为数字条件
	switch factor.Source {
	case DailySource:
		source = info.DailyStats
	case WeeklySource:
		source = info.WeeklyStats
	case MonthlySource:
		source = info.MonthlyStats
	default:
		source = info.AllStats
	}

	return factor.Judge(source)
}

func (info *StatInfo) GetStatsSingle(cycle utils.CycleKey, key string) int32 {
	var source map[string]int32
	switch cycle {
	case utils.DailyCycle:
		source = info.DailyStats
	case utils.WeeklyCycle:
		source = info.WeeklyStats
	case utils.MonthlyCycle:
		source = info.MonthlyStats
	default:
		source = info.AllStats
	}
	if v, ok := source[key]; ok {
		return v
	} else {
		return 0
	}
}

func (info *StatInfo) GetStats(context def.Context) error {
	if info.Stats {
		return nil
	}
	var ok bool
	info.Stats = true
	// 获取统计数据
	stats := info.GetStatData(context, info.id)
	info.AllStats, ok = stats[utils.AllCycle]
	if !ok {
		info.AllStats = map[string]int32{}
	}
	info.DailyStats, ok = stats[utils.DailyCycle]
	if !ok {
		info.DailyStats = map[string]int32{}
	}
	info.WeeklyStats, ok = stats[utils.WeeklyCycle]
	if !ok {
		info.WeeklyStats = map[string]int32{}
	}
	info.MonthlyStats, ok = stats[utils.MonthlyCycle]
	if !ok {
		info.MonthlyStats = map[string]int32{}
	}
	return nil
}

func (user *StatInfo) ChangeStats(context def.Context, key string, value int32, reset bool) error {
	if !user.Stats && user.Auto {
		if err := user.GetStats(context); err != nil {
			return err
		}
	}
	l := user.GetStatConfigByKey(key)
	var source map[string]int32
	// 根据统计要求，进行记录
	for _, stat := range l {
		switch stat.Cycle {
		case utils.DailyCycle:
			source = user.DailyStats
		case utils.WeeklyCycle:
			source = user.WeeklyStats
		case utils.MonthlyCycle:
			source = user.MonthlyStats
		default:
			source = user.AllStats
		}
		if reset {
			source[stat.Key] = value
		} else {
			stat.MergeStat(&source, value)
		}
	}
	if err := user.SaveStatData(context, user.id, key, value, reset); err != nil {
		return err
	}
	return nil
}

type StatData struct {
	StatItemModel *orm.Model
	// stats不会修改，保持引用即可
	Stats      map[string]*SettingStat
	StatKeyMap map[string][]utils.StatConfig

	// 统计相关
	GameStats []*SettingStat
}

func GetStatData(env *game.Environment) (*StatData, error) {
	var err error
	data := &StatData{}

	if _, _, data.Stats, err = getStatData(env); err != nil {
		return nil, err
	}
	model, err := env.GetModel(StatItemName)
	if err != nil {
		return nil, err
	}
	data.StatItemModel = model

	// 分离最大战斗统计，需合并统计项
	data.GameStats = make([]*SettingStat, 0)
	for key := range data.Stats {
		stats := data.Stats[key]
		if stats.IsMaxPlayer || (stats.IsPlaybook) {
			data.GameStats = append(data.GameStats, stats)
		}
	}
	data.StatKeyMap = map[string][]utils.StatConfig{}
	for _, stat := range data.Stats {
		if stat.Cycles == nil || len(stat.Cycles) == 0 {
			stat.Cycles = []utils.CycleKey{utils.AllCycle}
		}
		l := make([]utils.StatConfig, len(stat.Cycles))
		for index, cycle := range stat.Cycles {
			l[index] = utils.StatConfig{
				Key:   stat.Key,
				Cycle: cycle,
				Stat:  stat.Type,
			}
		}
		data.StatKeyMap[stat.Key] = l
		stat.Configs = l
	}

	return data, err
}

func (data *StatData) GetStatValue(key string, statInfo *StatInfo, cycle utils.CycleKey, d int32) (*SettingStat, int32) {
	statSetting, ok := data.Stats[key]
	if !ok {
		return nil, 0
	}
	value := d
	for _, c := range statSetting.Cycles {
		if c != cycle {
			continue
		}
		if len(statSetting.Dependents) > 0 {
			value = 0
			for _, dep := range statSetting.Dependents {
				value += statInfo.GetStatsSingle(cycle, dep)
			}
		}
	}
	return statSetting, value
}
func getStatData(env *game.Environment) (*orm.Model, []SettingStat, map[string]*SettingStat, error) {
	var err error
	// stat
	var stats []SettingStat

	model, err := env.GetModel(SettingStatName)
	if err != nil {
		return nil, nil, nil, err
	}
	_, err = model.Query().Find(&stats)
	if err != nil {
		return nil, nil, nil, err
	}
	statMap := make(map[string]*SettingStat, len(stats))
	for k := range stats {
		sort.Sort(stats[k].ThresholdConfig)
		// 建立trigger到stat的引用关系
		for index := range stats[k].TriggerConfig {
			trigger := &stats[k].TriggerConfig[index]
			if trigger.From != nil && len(trigger.From) > 0 {
				if trigger.From[0] > 0 {
					trigger.Priority += 1
					if len(trigger.From) > 1 && trigger.From[1] > 0 {
						trigger.Priority += 3
					}
				}
			}
			if trigger.Target != nil && len(trigger.Target) > 0 {
				if trigger.Target[0] > 0 {
					trigger.Priority += 2
					if len(trigger.Target) > 1 && trigger.Target[1] > 0 {
						trigger.Priority += 4
					}
				}
			}
			stats[k].TriggerConfig[index].Stat = &stats[k]

		}
		statMap[stats[k].Key] = &stats[k]
	}
	return model, stats, statMap, nil
}

func (data *StatData) GetStatData(context def.Context, id int) map[utils.CycleKey]map[string]int32 {
	var stats []StatItem
	if _, err := data.StatItemModel.Query().Where("=", "id", id).Find(&stats); err == nil {
		result := map[utils.CycleKey]map[string]int32{}
		for _, item := range stats {
			mm, ok := result[item.Cycle]
			if !ok {
				mm = map[string]int32{}
				result[item.Cycle] = mm
			}
			mm[item.Key] = item.Stats
		}
		return result
	}
	return map[utils.CycleKey]map[string]int32{}
}
func (data *StatData) SaveStatData(context def.Context, id int, key string, stats int32, reset bool) error {
	l := data.GetStatConfigByKey(key)
	for _, stat := range l {
		var operate string = ""
		var value interface{} = stats
		if reset {

		} else {
			switch stat.Stat {
			case utils.CountStat:
				value = orm.UpdateOperate{
					Operate: "+",
					Value:   stats,
				}
			case utils.MaxStat:
				operate = ">"
				value = stats
			case utils.MinStat:
				operate = "<"
				value = stats
			case utils.LastStat:
				value = stats
			}
		}

		q := data.StatItemModel.Query().Where("=", "id", id).Where("=", "cycle", stat.Cycle).Where("=", "key", key)
		if operate != "" {
			q.Where(operate, "stats", stats)
		}
		n, err := q.Update(map[string]interface{}{
			"stats": value,
		})
		if err != nil {
			return err
		}
		if n > 0 {
			continue
		}

		_, _ = data.StatItemModel.Query().Insert(map[string]interface{}{
			"stats": stats,
		}, map[string]interface{}{
			"key":   key,
			"stats": stats,
			"id":    id,
			"cycle": stat.Cycle,
		})
	}

	return nil
}

func (data *StatData) GetStatConfigByKey(key string) []utils.StatConfig {
	configs, ok := data.StatKeyMap[key]
	if !ok {
		return []utils.StatConfig{}
	}
	return configs
}

func (data *StatData) GetStatByKey(key string) (*SettingStat, utils.CycleKey) {
	pp := strings.Split(key, ":")
	setting, ok := data.Stats[pp[0]]
	//fmt.Println("stat", key, data.Stats, setting, ok)
	if !ok {
		return nil, utils.AllCycle
	}
	return setting, utils.CycleKey(utils.ToInt(pp[1]))
}
